package mviewer.renderer.software;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.image.BufferedImage;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Stack;
import mviewer.renderer.IRenderer;
import mviewer.renderer.software.core.Matrix3d;
import mviewer.renderer.software.core.ZBuffer;
import mviewer.renderer.software.interfaces.IBuffer;
import mviewer.renderer.software.interfaces.ILighting;
import mviewer.renderer.software.interfaces.IPerspective;
import mviewer.renderer.software.lighting.PhongLight;
import mviewer.renderer.software.perspective.ParallelPerspective;
import mviewer.renderer.software.swing.RendererView;
import mviewer.types.Camera;
import mviewer.types.IModel3d;
import mviewer.types.IDrawerContext;
import mviewer.types.IVector;
import mviewer.types.VectorData;

@Deprecated
public class SoftwareRenderer implements IRenderer {

	private Map<String, IModel3d> modelInstances;
	private RendererView component;
	private BufferedImage image;
	private IModel3d model;
	private Camera camera;
	private IPerspective perspective = new ParallelPerspective();
	private ILighting lighting = new PhongLight();
	private Stack<Matrix3d> matrices = new Stack<Matrix3d>();
	private boolean outputBufferImage = false, wireframe;
	private float worldScaling = 1;
	private IVector modelCenter;
	private int width, height;

	public void setOutputBufferImage(boolean outputBufferImage) {
		this.outputBufferImage = outputBufferImage;
	}

	public BufferedImage getImage() {
		return image;
	}

	public void setWireframe(boolean wireframe) {
		this.wireframe = wireframe;
	}

	@Override
	public void setModel(IModel3d model) {
		this.model = prepareModel(model);
	}

	private IModel3d prepareModel(IModel3d model) {
		modelCenter = model.calculateCenter();
		modelInstances = new HashMap<String, IModel3d>();
		IModel3d copy = model.clone(), src, cpy;
		Iterator<IModel3d> srcIter, cpyIter;
		Collection<IModel3d> sources = new LinkedList<IModel3d>(),
							 copies	 = new LinkedList<IModel3d>();

		model.listContainedModels(sources);
		copy.listContainedModels(copies);

		srcIter = sources.iterator();
		cpyIter = copies.iterator();

		while (srcIter.hasNext()) {
			src = srcIter.next();
			cpy = cpyIter.next();

			modelInstances.put(src.getName(), src);
			modelInstances.put(cpy.getName(), cpy);
		}

		return copy;
	}

	@Override
	public void setCamera(Camera camera) {
		this.camera = camera;
		this.lighting.setCamera(camera);
	}

	@Override
	public Component createComponent() {
		component = new RendererView(this);

		component.addComponentListener(new ResizeListener());

		return component;
	}

	@Override
	public void display() {
		renderImage();
		component.repaint();
	}

	synchronized private void renderImage() {
		Color background = new Color(BACKGROUND.getX(),
									 BACKGROUND.getY(),
									 BACKGROUND.getZ());

		if (width == 0 || height == 0)
			return;

		if (model == null) { // create empty image
			image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			Graphics2D g = image.createGraphics();

			g.setColor(background);
			g.fillRect(0, 0, width, height);
		} else { // transform model and render image
			BufferedImage res = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			Graphics2D g = res.createGraphics();
			Matrix3d matrix = new Matrix3d();
			IBuffer buffer = new ZBuffer(width, height, camera);
			IDrawerContext drawer = wireframe
										? new WireframeDrawer(lighting,
												perspective, camera, buffer, g,
												matrices, modelInstances)
										: new SimpleSoftwareDrawer(lighting,
												perspective, camera, buffer, g,
												matrices, modelInstances);

			g.setColor(background);
			g.fillRect(0, 0, width, height);
			matrix.unit();
			matrices.push(matrix);
			matrix.scale(worldScaling);
			matrix.translate(modelCenter.getX(), modelCenter.getY(), modelCenter.getZ());
			model.draw(drawer);
			matrices.pop();

			image = outputBufferImage ? buffer.renderImage() : res;
		}
	}


	private class ResizeListener implements ComponentListener {

		public void componentResized(ComponentEvent evt) {
			Component c = evt.getComponent();
			width = c.getWidth();
			height = c.getHeight();
			worldScaling = height / model.calculateDiagonalLength();
			modelCenter = model.calculateCenter();
			modelCenter = new VectorData(modelCenter.getX() * worldScaling,
										modelCenter.getY() * worldScaling,
										modelCenter.getZ() * worldScaling);

			display();
		}

		public void componentMoved(ComponentEvent ce) { }

		public void componentShown(ComponentEvent ce) { }

		public void componentHidden(ComponentEvent ce) { }

	}
}